/**
 * Copyright (C) 2011  Robin Dietrich, Nils Kenneweg, Leonard Stutz
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 **/

#include "config.hpp"

#if P_PLUGINS
#include "pluginManager.hpp"
#endif
#include "MainApplication.hpp"
#include "chunkManager.hpp"

#include <OgreOverlayManager.h>

bool operator==(coordinate const& p1, coordinate const& p2) {
  return p1.x == p2.x && p1.y == p2.y && p1.z == p2.z;
}

std::size_t hash_value(coordinate const& p){
  std::size_t seed =0;
  boost::hash_combine(seed, p.x);
  boost::hash_combine(seed, p.y);
  boost::hash_combine(seed, p.z);
  return seed;
}

namespace minecraft {

    MainApplication::MainApplication(void) {
        //just so we can be certain that an object exists
        #if P_PLUGINS
        sPluginManager::Instance();
        #endif
        sBlockManager::Instance();
        sChunkManager::Instance();
        lastPosition = NULL;
        renderer = NULL;
        update_chunk = false;
		box = NULL;
        inventory = NULL;        
        frames = NULL;
    }

    MainApplication::~MainApplication(void) {
        sChunkManager::Destroy();
        sBlockManager::Destroy();        
        #if P_PLUGINS
        sPluginManager::Destroy();
        #endif
    }
	
	bool MainApplication::mouseMoved( const OIS::MouseEvent &arg ) {
        if (!this->cegui_state) {
            mCameraMan->injectMouseMove(arg);
        }
		
		CEGUI::System &sys = CEGUI::System::getSingleton();
		sys.injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
		if (arg.state.Z.rel) sys.injectMouseWheelChange(arg.state.Z.rel / 120.0f);
			
		if (this->game_started) {
			chunkManager *cm = sChunkManager::Instance();
			
			Ogre::Vector3 pos = mCamera->getPosition();
			Ogre::Vector3 dir = mCamera->getDirection();
			Ogre::Vector3 *len = new Ogre::Vector3(0,0,0);
			Ogre::Vector3 *pick = new Ogre::Vector3(pos.x,pos.y,pos.z);
			
			dir /= 20;
			
			while((len->squaredLength()<25) && (cm->getBlock(std::floor(pick->x),std::floor(pick->y),std::floor(pick->z)) == 0)){
			  pick->x += dir.x; pick->y += dir.y; pick->z += dir.z;
			  len->x += dir.x; len->y += dir.y; len->z += dir.z;
			}
			
			//std::cout << pos << dir << " " << pick->x << ", " << pick->y << ", " << pick->z << "\n";
			
			if (len->squaredLength()<25) {
				if (this->box == NULL) {
					// create ManualObject
					Ogre::ManualObject* manual = mSceneMgr->createManualObject("block_selection_box");
					
					// specify the material (by name) and rendering type
					manual->begin("BaseBlackNoLighting", Ogre::RenderOperation::OT_LINE_LIST);
					 
					// define start and end point
					manual->position(0, 0, 0); //0
					manual->position(0, 0, 1); //1
					manual->position(0, 1, 0); //2
					manual->position(0, 1, 1); //3
					manual->position(1, 0, 0); //4
					manual->position(1, 0, 1); //5
					manual->position(1, 1, 0); //6
					manual->position(1, 1, 1); //7
					
					manual->index(0);
					manual->index(1);
					
					manual->index(0);
					manual->index(2);
					
					manual->index(0);
					manual->index(4);
					
					manual->index(1);
					manual->index(3);
					
					manual->index(1);
					manual->index(5);
					
					manual->index(2);
					manual->index(3);
					
					manual->index(2);
					manual->index(6);
					
					manual->index(3);
					manual->index(7);
					
					manual->index(4);
					manual->index(5);
					
					manual->index(4);
					manual->index(6);
					
					manual->index(5);
					manual->index(7);
					
					manual->index(6);
					manual->index(7);
					
					 
					// tell Ogre, your definition has finished
					manual->end();
					 
					this->box = mSceneMgr->getRootSceneNode()->createChildSceneNode(Ogre::Vector3(pick->x, pick->y, pick->z));
					this->box->attachObject(manual);

					//put stuff in lists
					this->activeNodes.push_back(this->box);
				} else {
					this->box->setPosition(std::floor(pick->x), std::floor(pick->y), std::floor(pick->z));
				}
			
				//this->box->setVisible(true);
			} else {
				//this->box->setVisible(false);
			}
			
			delete(len);
			delete(pick);
		}
		
		return true;
	}
	
	float MainApplication::getIntersection(float fDst1, float fDst2, Ogre::Vector3 *P1, Ogre::Vector3 *P2){
	  if ((fDst1*fDst2)>=0.0f) return 0;
	  if (fDst1==fDst2) return 0; 
	  Ogre::Vector3 hit = *P1 + (P2-P1) * (-fDst1/(fDst2-fDst1));
	  std::cout << "Intersection x:" << hit.x << ", y:" << hit.y << ", z:" << hit.z << "\n";
	  return 1;
	}
	
	bool MainApplication::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) {
        if (!this->cegui_state) {
            mCameraMan->injectMouseDown(arg, id);
		
			chunkManager *cm = sChunkManager::Instance();
			std::cout << "id: " << id << "\n";
			Ogre::Vector3 pos = mCamera->getPosition();
			Ogre::Vector3 dir = mCamera->getDirection();
			Ogre::Vector3 *len = new Ogre::Vector3(0,0,0);
			Ogre::Vector3 *pick = new Ogre::Vector3(pos.x,pos.y,pos.z);
						
			dir /= 20;
			
			while((len->squaredLength()<25) && (cm->getBlock(std::floor(pick->x),std::floor(pick->y),std::floor(pick->z)) == 0)){
			  pick->x += dir.x; pick->y += dir.y; pick->z += dir.z;
			  len->x += dir.x; len->y += dir.y; len->z += dir.z;
			}
			
			if (len->squaredLength()<25) {
				//std::cout << pos << ";" << dir << ";" << pick->x << "," << pick->y << "," << pick->z << " " << cm->getBlock(pick->x,pick->y,pick->z) << "\n";
				
				if(id == 0)
				  sChunkManager::Instance()->setBlock(std::floor(pick->x),std::floor(pick->y),std::floor(pick->z),0);
				
				int x = pick->x;
				int y = pick->y;
				int z = pick->z;
				
				if(x<0) x-=1;
				if(z<0) z-=1;
				
				std::cout << "Pick coordinates: " << x << ", " << y << ", " << z << "\n";
				
				localCoordinates coordinates = sChunkManager::Instance()->global2LocalCoordinates(x,y,z);
				
				std::list<Ogre::SceneNode*>::iterator it;
				for (it=activeNodes.begin(); it!=activeNodes.end(); ++it){
					Ogre::MovableObject *chunkMesh = (*it)->getAttachedObject(0);
					
					if (chunkMesh->getName() == "chunk_" + coordinatesToChunkId(coordinates.chunk.x, coordinates.chunk.y, coordinates.chunk.z)) {
						//update the chunkMesh.
						break;
					}
				}
				
				localCoordinates cx, cy, cz;
				if(x < 0){
				  if((x-1) % P_CHUNKSIZE == 0)
					cx = sChunkManager::Instance()->global2LocalCoordinates(x-2,y,z);	
				  if((x-1) % P_CHUNKSIZE-1 == 0)
					cx = sChunkManager::Instance()->global2LocalCoordinates(x+1,y,z);	
				}
				else if(x > 0){
				  if(((x+1) % P_CHUNKSIZE) == 0)
					cx = sChunkManager::Instance()->global2LocalCoordinates(x+1,y,z);	
				  else if(x % P_CHUNKSIZE == 0)
					cx = sChunkManager::Instance()->global2LocalCoordinates(x-1,y,z);	
				}
				
				if(y < 0){
				  if((y-1) % P_CHUNKSIZE == 0)
					cy = sChunkManager::Instance()->global2LocalCoordinates(x,y-2,z);	
				  if((y-1) % P_CHUNKSIZE-1 == 0)
					cy = sChunkManager::Instance()->global2LocalCoordinates(x,y+1,z);	
				}
				else if(y > 0){
				  if(((y+1) % P_CHUNKSIZE) == 0)
					cy = sChunkManager::Instance()->global2LocalCoordinates(x,y+1,z);	
				  else if(y % P_CHUNKSIZE == 0)
					cy = sChunkManager::Instance()->global2LocalCoordinates(x,y-1,z);	
				}
				
				if(z < 0){
				  if((z-1) % P_CHUNKSIZE == 0)
					cz = sChunkManager::Instance()->global2LocalCoordinates(x,y,z-2);	
				  if((z-1) % P_CHUNKSIZE-1 == 0)
					cz = sChunkManager::Instance()->global2LocalCoordinates(x,y,z+1);	
				}
				else if(z > 0){
				  if(((z+1) % P_CHUNKSIZE) == 0)
					cz = sChunkManager::Instance()->global2LocalCoordinates(x,y,z+1);	
				  else if(z % P_CHUNKSIZE == 0)
					cz = sChunkManager::Instance()->global2LocalCoordinates(x,y,z-1);	
				}
				
				  this->updateList_lock.lock();
					this->updateList.insert(cx.chunk);
					this->updateList.insert(cy.chunk);
					this->updateList.insert(cz.chunk);
				  this->updateList_lock.unlock();

				if(id == 1){
				  Ogre::Vector3 *new_block_pos = new Ogre::Vector3(x,y,z);
				  float dx0 = abs(pick->x - x);
				  float dx1 = abs(pick->x - (x+1));
				  float dy0 = abs(pick->y - y);
				  float dy1 = abs(pick->y - (y+1));
				  float dz0 = abs(pick->z - z);
				  float dz1 = abs(pick->z - (z+1));
				  if(dx0 < dx1 && dx0 < dy0 && dx0 < dy1 && dx0 < dz0 && dx0 < dz1)
					  new_block_pos->x -= 1; //std::cout << "hitting near x plane\n";
				  else if(dx1 < dx0 && dx1 < dy0 && dx1 < dy1 && dx1 < dz0 && dx1 < dz1)
					  new_block_pos->x += 1; //std::cout << "hitting far x plane\n";
				  else if(dy0 < dx0 && dy0 < dx1 && dy0 < dy1 && dy0 < dz0 && dy0 < dz1)
					  new_block_pos->y -= 1; //std::cout << "hitting near y plane\n";
				  else if(dy1 < dx0 && dy1 < dx1 && dy1 < dy0 && dy1 < dz0 && dy1 < dz1)
					  new_block_pos->y += 1; //std::cout << "hitting far y plane\n";
				  else if(dz0 < dx0 && dz0 < dx1 && dz0 < dy0 && dz0 < dy1 && dz0 < dz1)
					  new_block_pos->z -= 1; //std::cout << "hitting near z plane\n";
				  else if(dz1 < dx0 && dz1 < dx1 && dz1 < dy0 && dz1 < dy1 && dz1 < dz0)
					  new_block_pos->z += 1; //std::cout << "hitting far z plane\n";
				  sChunkManager::Instance()->setBlock(new_block_pos->x,new_block_pos->y,new_block_pos->z,this->mCameraMan->getSelectedBlock());
				}

				this->updateList_lock.lock();
				  this->updateList.insert(coordinates.chunk);
				this->updateList_lock.unlock();
				this->update_chunk = true;
				  
			}
			
			
			

			
			delete(len);
			delete(pick);
		}
		
        CEGUI::System::getSingleton().injectMouseButtonDown(convertButton(id));
		return true;
	}		

    void MainApplication::createScene(void) {
        CEGUI::WindowManager *wm = CEGUI::WindowManager::getSingletonPtr();
        
        //menu background
        CEGUI::Window *menu = wm->createWindow("TaharezLook/StaticImage", "minecraft/menu");
        menu->setPosition( CEGUI::UVector2( CEGUI::UDim( 0.0f, 0.0f ), CEGUI::UDim( 0.0f, 0.0f) ) );
        menu->setSize( CEGUI::UVector2( CEGUI::UDim( 1.0f, 0.0f ), CEGUI::UDim( 1.0f, 0.0f ) ) );        
        CEGUI::ImagesetManager::getSingleton().createFromImageFile("minecraft/menu_bg", "menu.png");
        menu->setProperty( "Image", "set:minecraft/menu_bg image:full_image" );  
        
        //start button
        CEGUI::PushButton* menu_s = (CEGUI::PushButton*)wm->createWindow("TaharezLook/Button", "menu/start");
        menu->addChildWindow( menu_s );
        menu_s->setText("Start");
        menu_s->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&MainApplication::startEvt, this));
        menu_s->setPosition( CEGUI::UVector2( CEGUI::UDim( 0.03f, 0.0f), CEGUI::UDim( 0.03f, 0.0f ) ) );
        menu_s->setSize( CEGUI::UVector2( CEGUI::UDim( 0.1f, 0.0f ), CEGUI::UDim( 0.05f, 0.0f ) ) );
        
        //quit button
        CEGUI::PushButton* menu_q = (CEGUI::PushButton*)wm->createWindow("TaharezLook/Button", "menu/quit");
        menu->addChildWindow( menu_q );
        menu_q->setText("Quit");
        menu_q->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&BaseApplication::quitButtonEvtY, dynamic_cast<BaseApplication*>(this)));
        menu_q->setPosition( CEGUI::UVector2( CEGUI::UDim( 0.03f, 0.0f), CEGUI::UDim( 0.10f, 0.0f ) ) );
        menu_q->setSize( CEGUI::UVector2( CEGUI::UDim( 0.1f, 0.0f ), CEGUI::UDim( 0.05f, 0.0f ) ) );
        
        CEGUI::System::getSingletonPtr()->setGUISheet(menu);        
    }
    
    bool MainApplication::startEvt(const CEGUI::EventArgs &e) {
        //destroy menu
        this->cegui_state = false;
		this->game_started = true;
        CEGUI::MouseCursor::getSingleton().hide();
        CEGUI::WindowManager::getSingleton().destroyAllWindows();
        
        #if P_PLUGINS
        //load plugins
        std::string filename = std::string(P_ROOT)+std::string("conf/McPlugins.cfg");
        std::ifstream file(filename.c_str());
        std::string tmp;
        while (std::getline(file, tmp, ',')) {
            sPluginManager::Instance()->load(tmp);
        }
        file.close();
        #endif
		
		int z = 0;
		
		for(z = 0; sChunkManager::Instance()->getBlock(32,z,60) != 0; ++z) {
		}
		mCamera->setPosition(Ogre::Vector3(32,z+2,60));
        
		//TODO: Load Basic Chunk Set around player.		
        
        
        //skydome
        mSceneMgr->setSkyDome(true, "minecraft/SkyDome", 2, 8, 100); //skype dome
        mSceneMgr->setFog(Ogre::FOG_LINEAR, Ogre::ColourValue(0.5, 0.5, 0.5), 0.05, 0.0, 200); //grey fog
        mCamera->setFarClipDistance(P_CHUNKSIZE * 16); //view as much as 8 chunks
        mCamera->setNearClipDistance(0.01);

        //get cursor image => material
        Ogre::MaterialPtr cMaterial = Ogre::MaterialManager::getSingleton().create("cursor", "minecraft");
        cMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("cursor.png");
        cMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
        cMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
        cMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
        cMaterial->getTechnique(0)->getPass(0)->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);

        //overlay container for the cursor
        Ogre::OverlayManager& overlayManager = Ogre::OverlayManager::getSingleton();
        Ogre::OverlayContainer* cursor = static_cast<Ogre::OverlayContainer*> (overlayManager.createOverlayElement("Panel", "cursor"));
        cursor->setMetricsMode(Ogre::GMM_PIXELS);
        cursor->setHorizontalAlignment(Ogre::GHA_CENTER);
        cursor->setVerticalAlignment(Ogre::GVA_CENTER);
        cursor->setDimensions(9, 9);
        cursor->setMaterialName("cursor");
        
        //create an overlay, and add the cursor
        Ogre::Overlay* overlay = overlayManager.create("main_overlay");       
        overlay->add2D(cursor);
        overlay->show();
        
        Ogre::Light* directionalLight = mSceneMgr->createLight("directionalLight");
        directionalLight->setType(Ogre::Light::LT_DIRECTIONAL);
        directionalLight->setDiffuseColour(Ogre::ColourValue(.25, .25, 0));
        directionalLight->setSpecularColour(Ogre::ColourValue(.25, .25, 0));
        directionalLight->setDirection(Ogre::Vector3(0, -1, 1));

		new boost::thread(boost::bind(&MainApplication::getWorker, this));
		this->started = false;
		this->rounds = 0;
		this->RenderHelperTime = 50;
        
        mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));
        Ogre::Light* l = mSceneMgr->createLight("MainLight");
        l->setPosition(0, 0, 0); 
        
        return true;
    }

	void MainApplication::getAdd(int x, int y, int z) {
		this->getAdd(x,y,z,false);
	}
	
	void MainApplication::getAdd(int x, int y, int z, bool doUpdate) {
		//boost::thread* thread = new boost::thread(boost::bind(&MainApplication::getWorker, this, x, y, z));
        
        coordinate c;
        c.x = x;
        c.y = y;
        c.z = z;
		
		std::string id = coordinatesToChunkId(x, y, z);
		
		this->getList_lock.lock();
			//change to some kind of hashmap so we can find the
			//current coordinate faster.
			if (doUpdate || this->rendering.find(id) == this->rendering.end()) {
				this->rendering.insert(id);
				this->getList.insert(c);
			}
		this->getList_lock.unlock();
	}
	
	void MainApplication::getWorker() {
		//take one from the list and get the chunks for it.
		int x,y,z = 0;
		
		while(!this->mShutDown) {
			bool doGet = false;
			boost::thread::yield();
			
			this->getList_lock.lock();
				boost::unordered_set<coordinate>::const_iterator toGet;
				boost::unordered_set<coordinate>::const_iterator GetIter = getList.begin();
				
				unsigned int mDiff = 300;
				
				/*if (GetIter != getList.end()) {
					std::cout << "Position:" << this->currentPosX << ":" << this->currentPosY << ":" << this->currentPosZ << "\n";
				}*/
				
				while(GetIter != getList.end()) {
					x = GetIter->x;
					y = GetIter->y;
					z = GetIter->z;
					
					int dx = this->currentPosX - x;
					int dy = this->currentPosY - y;
					int dz = this->currentPosZ - z;
					
					unsigned int diff = dx * dx + dy * dy + dz * dz;
					
					//std::cout << "Diff:" << diff << ";" << x << ":" << y << ":" << z << "\n";
					
					if (diff > 250) {
						//std::cout << "Removed: (" << diff << ") " << x << ":" << y << ":" << z << "\n";
						GetIter = getList.erase(GetIter);
					} else {
						if (diff < mDiff) {
							toGet = GetIter;
							mDiff = diff;
							
							doGet = true;
							
							if (mDiff == 0) {
								break;
							}
						}
						
						GetIter++;
					}
				}
					
				if (doGet) {
					x = toGet->x;
					y = toGet->y;
					z = toGet->z;
					
					//std::cout << "Choosen:" << x << ":" << y << ":" << z << "\n";
						
					getList.erase(toGet);
				} else {
					 boost::thread::yield();
				}
			this->getList_lock.unlock();
			
			boost::thread::yield();
			
			if (doGet) {
				//std::cout << "Get: " << x << ":" << y << ":" << z << "\n";
				
				sChunkManager::Instance()->getChunk(x+1, y, z);
				boost::thread::yield();
				sChunkManager::Instance()->getChunk(x-1, y, z);
				boost::thread::yield();
			
				sChunkManager::Instance()->getChunk(x, y+1, z);
				boost::thread::yield();
				sChunkManager::Instance()->getChunk(x, y-1, z);
				boost::thread::yield();
			
				sChunkManager::Instance()->getChunk(x, y, z+1);
				boost::thread::yield();
				sChunkManager::Instance()->getChunk(x, y, z-1);
				boost::thread::yield();
			
				sChunkManager::Instance()->getChunk(x, y, z);
				boost::thread::yield();
			
				coordinate c;
				c.x = x;
				c.y = y;
				c.z = z;
			
				this->renderList_lock.lock();
					this->renderList.insert(c);
				this->renderList_lock.unlock();			
			}
		}
	}
	
	void MainApplication::doDraw(int x, int y, int z) {
	  coordinate c;
	  c.x = x;
	  c.y = y;
	  c.z = z;
	  if(activeList.find(c) != activeList.end() && !this->update_chunk)
		return;
	  activeList[c] = true;
	  this->getAdd(x, y, z);
	}
	
	void MainApplication::doRendering()
	{
		unsigned long long t1;
		unsigned long long t2;
		
		if (this->renderer != NULL && this->renderer->done) {
			//std::cout << "Rendering finished\n";
		
			this->drawList.insert(this->renderer->rChunk);
			delete this->renderer;
			
			this->renderer = NULL;
		}
	
		if (this->renderer == NULL) {
			int rx, ry, rz = 0;
			bool doRender = false;
			
			renderList_lock.lock();
				boost::unordered_set<coordinate>::const_iterator toRender = renderList.begin();
				boost::unordered_set<coordinate>::const_iterator RenderIter = renderList.begin();
				
				unsigned int mDiff = 300;
				
				if (toRender != renderList.end()) {
					while(RenderIter != renderList.end()) {
						rx = RenderIter->x;
						ry = RenderIter->y;
						rz = RenderIter->z;
						
						int dx = this->currentPosX - rx;
						int dy = this->currentPosY - ry;
						int dz = this->currentPosZ - rz;
						
						unsigned int diff = dx * dx + dy * dy + dz * dz;
						
						if (diff > 250) {
							std::cout << "Removed ( " << diff << " ): " << rx << ":" << ry << ":" << rz << "\n";
							RenderIter = renderList.erase(RenderIter);
						} else {
							if (diff < mDiff) {
								toRender = RenderIter;
								mDiff = diff;
							}
							
							RenderIter++;
						}						
					}
					doRender = true;
					rx = toRender->x;
					ry = toRender->y;
					rz = toRender->z;
					
					renderList.erase(toRender);
				}
			renderList_lock.unlock();
			
			if (doRender) {
				t1 = rdtsc();
			
				this->renderer = new RenderHelper(rx, ry, rz, this->RenderHelperTime);
				Call(this->renderer);
				
				t2 = rdtsc();
				//std::cout << "Rendering Part1:" << (t2-t1) / 1000 << "\n";
			}
		} else {
			t1 = rdtsc();
				Call(this->renderer);
			t2 = rdtsc();
			//std::cout << "Rendering Part2:" << (t2-t1) / 1000 << "\n";
		}
		
		return;
	}
    
    bool MainApplication::keyPressed( const OIS::KeyEvent &arg ) {
        BaseApplication::keyPressed(arg);
        
        if (arg.key == OIS::KC_I) {             
            if (!this->cegui_state && this->game_started && !this->frames) {
                //create inventory
                CEGUI::WindowManager *wm = CEGUI::WindowManager::getSingletonPtr();
                
                //size of inventory
                unsigned short rows = sBlockManager::Instance()->getAvailable().size() / 8;
                if (rows == 0 | (sBlockManager::Instance()->getAvailable().size() % 8) != 0) {
                    ++rows;
                }
        
                //inventory frame
                this->inventory = wm->createWindow("TaharezLook/FrameWindow", "minecraft/inventory");
                this->inventory->setPosition( CEGUI::UVector2( CEGUI::UDim( 0.25f, 0.0f ), CEGUI::UDim( 0.25f, 0.0f) ) );
                this->inventory->setSize( CEGUI::UVector2( CEGUI::UDim( 0.5f, 0.0f ), CEGUI::UDim( 0.5f, 0.0f ) ) );   
                this->inventory->setText("Inventory");
                
                //scrollable pane
                CEGUI::ScrollablePane* inv_c = (CEGUI::ScrollablePane*)wm->createWindow("TaharezLook/ScrollablePane", "inventory/container");
                inv_c->setPosition( CEGUI::UVector2( CEGUI::UDim( 0.0f, 0.0f ), CEGUI::UDim( 0.0f, 0.0f) ) );
                inv_c->setSize( CEGUI::UVector2( CEGUI::UDim( 1.0f, 0.0f ), CEGUI::UDim( 1.0f, 0.0f ) ) );   
                this->inventory->addChildWindow( inv_c );
                
                //add blocks
                blockManager::cList blocks = sBlockManager::Instance()->getAvailable();
                
                float pos_x = 0.05f;
                float pos_y = 0.025f;
                for (unsigned int i = 0; i < blocks.size(); ++i) {
                    //imagebutton
                    std::string b_cur = boost::lexical_cast<std::string>(blocks[i]);
                    
                    if ((i % 4) == 0 && (i != 0)) {
                        //6 items per row
                        pos_x = 0.05f;
                        pos_y += 0.225f;
                    }
                    
                    CEGUI::Window *tmp_bt = wm->createWindow("TaharezLook/StaticImage", "inventory/button_"+b_cur);
                    tmp_bt->setPosition( CEGUI::UVector2( CEGUI::UDim( pos_x, 0.0f ), CEGUI::UDim( pos_y, 0.0f) ) );
                    tmp_bt->setSize( CEGUI::UVector2( CEGUI::UDim( 0.2f, 0.0f ), CEGUI::UDim( 0.2f, 0.0f ) ) );    
                    tmp_bt->subscribeEvent(CEGUI::Window::EventMouseClick, CEGUI::Event::Subscriber(&MainApplication::invEvt, this));
                    
                    pos_x += 0.23f;                    
                    boost::shared_ptr<block> b_class = sBlockManager::Instance()->create(blocks[i]);
                    
                    CEGUI::ImagesetManager::getSingleton().createFromImageFile("inventory/buttonbg_"+b_cur, b_class->getTexturePath());
                    tmp_bt->setProperty( "Image", "set:inventory/buttonbg_"+b_cur+" image:full_image" );  
                    
                    inv_c->addChildWindow(tmp_bt);
                }
                
                //display
                this->cegui_state = 1;
                CEGUI::MouseCursor::getSingleton().show();
                CEGUI::System::getSingletonPtr()->setGUISheet(this->inventory);
            }
        } else if (arg.key == OIS::KC_F) {
			if (!this->frames && !this->cegui_state) {
				//create inventory
                CEGUI::WindowManager *wm = CEGUI::WindowManager::getSingletonPtr();
                
                this->frames = wm->createWindow("TaharezLook/FrameWindow", "minecraft/frames");
                this->frames->setPosition( CEGUI::UVector2( CEGUI::UDim( 0.05f, 0.0f ), CEGUI::UDim( 0.05f, 0.0f) ) );
                this->frames->setSize( CEGUI::UVector2( CEGUI::UDim( 0.2f, 0.0f ), CEGUI::UDim( 0.00001f, 0.0f ) ) );
                
                CEGUI::System::getSingletonPtr()->setGUISheet(this->frames);        
			} else if (this->frames) {
				CEGUI::WindowManager::getSingleton().destroyAllWindows();
				this->frames = NULL;
				this->cegui_state = 0;
			}
		}
    }
    
    bool MainApplication::invEvt(const CEGUI::EventArgs &e) {
        const CEGUI::MouseEventArgs* args = static_cast<const CEGUI::MouseEventArgs*>(&e);
        this->mCameraMan->setSelectedBlock(boost::lexical_cast<b_type>(args->window->getName().substr(17)));
       
        this->cegui_state = false;
        CEGUI::MouseCursor::getSingleton().hide();
        CEGUI::WindowManager::getSingleton().destroyAllWindows();
    }
	
    bool MainApplication::frameRenderingQueued(const Ogre::FrameEvent& evt) {	
		int RenderDist = 4;
	
        bool val = BaseApplication::frameRenderingQueued(evt);
		
		if (mWindow->getLastFPS() != 0) {
			if (mWindow->getLastFPS() < 40) {
				//std::cout << "LOW! \n";
			}
			
			if (this->renderer != NULL) {
				if (mWindow->getLastFPS() > 100) {
					if (this->RenderHelperTime < 100) {
						this->RenderHelperTime++;
					}
				} else if (mWindow->getLastFPS() < 40) {
					if (this->RenderHelperTime > 10) {
						this->RenderHelperTime - 5;
					}
				} else if (mWindow->getLastFPS() < 60) {
					if (this->RenderHelperTime > 5) {
						this->RenderHelperTime--;
					}
				}
			}
		
			if (this->game_started) {
				//std::cout << "FPS:" << mWindow->getLastFPS() << " Average: "<< mWindow->getAverageFPS() <<"\n";
			} else {
				//Sleep(17);
			}
			
			if (this->frames) {			
				this->frames->setText(boost::lexical_cast<std::string>(mWindow->getLastFPS()));
			}
		}
		
        if (!val)
            return false;

		this->rounds++;
			
        Ogre::Vector3 pos = mCamera->getPosition();
        int px = pos.x / P_CHUNKSIZE;
        int py = pos.y / P_CHUNKSIZE;
        int pz = pos.z / P_CHUNKSIZE;
	
	//check if c_x == 0 but x < 0 && x > -64
        if (pos.x < 0 && px == 0) px = -1;
        if (pos.y < 0 && py == 0) py = -1;
        if (pos.z < 0 && pz == 0) pz = -1;
	
		unsigned long long t1;
		unsigned long long t2;
	
		bool drawn = false;
	
		boost::unordered_set<Ogre::ManualObject*>::const_iterator toDraw = drawList.begin();
		if (toDraw != drawList.end()) {
			drawn = true;
			
			std::vector<std::string> parts = split((*toDraw)->getName().substr(6), '.', false);
			for(int i = 0; i < 3; ++i)
				std::replace(parts[i].begin(), parts[i].end(), 'F', '-');
			
			int x = boost::lexical_cast<int>(parts[0]);
			int y = boost::lexical_cast<int>(parts[1]);
			int z = boost::lexical_cast<int>(parts[2]);

			std::list<Ogre::SceneNode*>::iterator it;
			for (it=activeNodes.begin(); it!=activeNodes.end(); ++it){
				Ogre::MovableObject *chunkMesh = (*it)->getAttachedObject(0);
				
				if (chunkMesh->getName().substr(0,5) == "chunk") {
					std::vector<std::string> parts = split(chunkMesh->getName().substr(6), '.', false);
					for(int i = 0; i < 3; ++i)
						std::replace(parts[i].begin(), parts[i].end(), 'F', '-');
						
					if(boost::lexical_cast<int>(parts[0]) == x && boost::lexical_cast<int>(parts[1]) == y && boost::lexical_cast<int>(parts[2]) == z){
						std::cout << "deleting chunk_mesh" << chunkMesh->getName() << " because of refresh\n";
						(*it)->detachObject(chunkMesh->getName());
						delete chunkMesh;
						mSceneMgr->getRootSceneNode()->removeAndDestroyChild((*it)->getName());
						it = activeNodes.erase(it);
						coordinate c0;
						c0.x=x;
						c0.y=y;
						c0.z=z;
						activeList.erase(c0);
					}
				}
			}
			
			Ogre::SceneNode* activeNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(Ogre::Vector3(x*P_CHUNKSIZE, y*P_CHUNKSIZE, z*P_CHUNKSIZE));
			activeNode->attachObject((*toDraw));

			//put stuff in lists
			this->activeChunks.push_back((*toDraw));
			this->activeNodes.push_back(activeNode);
			
			drawList.erase(toDraw);
		}
		
		if (!drawn) {
			Sequencing(this->doRendering());
		}
	
		Ogre::Vector3 *playerPos = new Ogre::Vector3(px,py,pz);
	
        if (activeNodes.empty() && !this->started) {
			std::cout << "Startup position:" << px << ":" << py << ":" << pz << "\n";
            lastPosition = new Ogre::Vector3(px, py, pz);
			
			this->currentPosX = px;
			this->currentPosY = py;
			this->currentPosZ = pz;

			coordinate theCurrentPos;
			theCurrentPos.x = px;
			theCurrentPos.y = py;
			theCurrentPos.z = pz;
			
			sChunkManager::Instance()->setCurrentPosition(theCurrentPos);
			
			for(int x = px-RenderDist; x <= px+RenderDist; ++x){
			  for(int y = py-RenderDist; y <= py+RenderDist; ++y){
				for(int z = pz-RenderDist; z <= pz+RenderDist; ++z){
					this->started = true;
					this->doDraw(x, y, z);
				}
			  }
			}
        }
        
        if (px != lastPosition->x || py != lastPosition->y || pz != lastPosition->z || update_chunk) {
			this->currentPosX = px;
			this->currentPosY = py;
			this->currentPosZ = pz;
			
			coordinate update_coord;
			if(update_chunk){
			  boost::unordered_set<coordinate>::const_iterator abc = updateList.begin();
			  while(abc != updateList.end()){
				update_coord = (coordinate)*abc++;
				//std::cout << "UPDATING chunk at " << update_coord.x << " " << update_coord.y << " " << update_coord.z << "\n";
				this->getAdd(update_coord.x,update_coord.y,update_coord.z,true);
			  }
			}
			
			std::list<Ogre::SceneNode*>::iterator it;
			
			int deletecount = 0;
			
			for (it=activeNodes.begin(); it!=activeNodes.end(); ++it){
				Ogre::MovableObject *chunkMesh = (*it)->getAttachedObject(0);
				
				if (chunkMesh->getName().substr(0,5) == "chunk") {
					std::vector<std::string> parts = split(chunkMesh->getName().substr(6), '.', false);
					for(int i = 0; i < 3; ++i)
						std::replace(parts[i].begin(), parts[i].end(), 'F', '-');
						
					Ogre::Vector3 *chunkPos = new Ogre::Vector3(boost::lexical_cast<int>(parts[0]),boost::lexical_cast<int>(parts[1]),boost::lexical_cast<int>(parts[2]));
					coordinate c;
					c.x = chunkPos->x;
					c.y = chunkPos->y;
					c.z = chunkPos->z;
					if(chunkPos->squaredDistance(*playerPos) > 200){
						std::cout << "deleting chunk_mesh" << chunkMesh->getName() << " at " << *chunkPos << " distance "<< chunkPos->squaredDistance(*playerPos) <<"\n";
						(*it)->detachObject(chunkMesh->getName());
						delete chunkMesh;
						mSceneMgr->getRootSceneNode()->removeAndDestroyChild((*it)->getName());
						it = activeNodes.erase(it);
						
						this->getList_lock.lock();
							std::string id = coordinatesToChunkId(c.x, c.y, c.z);
							boost::unordered_set<std::string>::iterator toDelete = this->rendering.find(id);
		
							if (toDelete != this->rendering.end()) {
								this->rendering.erase(toDelete);
							}
						this->getList_lock.unlock();
						
						deletecount++;
					}

					delete chunkPos;
				}
			}
			
			if(update_chunk){
			  this->updateList_lock.lock();
			  this->updateList.clear();
			  this->updateList_lock.unlock();
			  update_chunk = false;
			}
		  
		  Ogre::Vector3 *dp = new Ogre::Vector3(px - lastPosition->x, py - lastPosition->y, pz - lastPosition->z);
		  

		  //std::cout << "delta position: " << *dp << "\n";
		  
		  if(dp->x != 0){
			int x = px + RenderDist * dp->x;
			for(int y = py-RenderDist; y <= py+RenderDist; ++y){
				for(int z = pz-RenderDist; z <= pz+RenderDist; ++z){
					this->doDraw(x,y,z);
				}
			}
		  }
		  
			if(dp->y != 0){
				for(int x = px-RenderDist; x <= px+RenderDist; ++x){
					int y = py + RenderDist * dp->y;
					for(int z = pz-RenderDist; z <= pz+RenderDist; ++z){
						this->doDraw(x,y,z);
					}
				}
			}

			if(dp->z != 0){
				for(int x = px-RenderDist; x <= px+RenderDist; ++x){
					for(int y = py-RenderDist; y <= py+RenderDist; ++y){
						int z = pz + RenderDist * dp->z;
							this->doDraw(x,y,z);
					}
				}
			}
			mSceneMgr->getRootSceneNode()->_update(true,false);
			mSceneMgr->getRootSceneNode()->_updateBounds();
			delete dp;
			lastPosition->x = px;
			lastPosition->y = py;
			lastPosition->z = pz;
        }
	
		delete playerPos;
	
        return val;
    }
}

using namespace minecraft;

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else

    int main(int argc, char *argv[])
#endif
    {
        // Create application object
        MainApplication app;

        try {
            app.go();
        } catch (Ogre::Exception& e) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
            std::cerr << "An exception has occured: " <<
                e.getFullDescription().c_str() << std::endl;
#endif
        }

        return 0;
    }

#ifdef __cplusplus
}
#endif
